﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Yappi.Delegates;

namespace Yappi.Samples
{
    /// <summary>
    /// Base for implementing interfaces with properties.
    /// </summary>
    public class DictionaryBased
    {
        //container for storing property values
        private readonly Dictionary<String, Object> _data = new Dictionary<string, object>();
        private readonly string _defaultstringValue;

        /// <summary>
        /// Ctor can be protected.
        /// All non-private constructors will be implemented in autogenrated proxy with same signature.
        /// Will be avaliable with Property.Constructor.Compile method.
        /// </summary>
        /// <param name="defaultString"> Sample .Ctor with parameter</param>
        protected DictionaryBased(string defaultString)
        {
            _defaultstringValue = defaultString;
        }

        /// <summary>
        /// Second .ctor.
        /// </summary>
        protected DictionaryBased()
        {
            _defaultstringValue = "";
        }

        /// <summary>
        /// Displays data stored in dictionary.
        /// </summary>
        public void WriteData()
        {
            //writing property values from container
            foreach (var pair in _data)
            {
                Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
            }
        }


        /// <summary>
        /// Implementation of properties.
        /// Must be IImplementationMap[TBaseType] and new()
        /// Defined as nested type for accessing private members of DictionaryBased
        /// </summary>
        public class Implementation : DefaultImplementation<DictionaryBased>
        {
            /// <summary>
            /// Defines custom property getter logic.
            /// Called once for each implementing property in type initializer (cctor).
            /// </summary>
            /// <typeparam name="TResult">Type of property value.</typeparam>
            /// <typeparam name="TDeclaringType">Type? declaring property.</typeparam>
            /// <typeparam name="TConstructedType">Constructed new type.</typeparam>
            /// <typeparam name="TBaseType">Will be DictionaryBased at run time.</typeparam>
            /// <param name="property">Name of the property.</param>
            /// <returns>Delegate implementing custom logic.</returns>
            public override Func<TBaseType, TResult> ImplementGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                var propertyName = property.Name;

                if (typeof(TResult) == typeof(string))
                {
                    //lambdas are easy to debug and implement.
                    return (pthis) =>
                    {
                        object value = null;
                        if (pthis._data.TryGetValue(propertyName, out value))
                        {
                            return (TResult)value;
                        }
                        return (TResult)(object)(pthis._defaultstringValue);
                    };

                }
                //lambdas are easy to debug and implement.
                return (pthis) =>
                {
                    object value = null;
                    if (pthis._data.TryGetValue(propertyName, out value))
                    {
                        return (TResult)value;
                    }
                    return default(TResult);
                };
            }

            /// <summary>
            /// Defines custom property setter logic.
            /// Called once for each implementing property in type initializer (cctor).
            /// </summary>
            /// <typeparam name="TResult">Type of property value.</typeparam>
            /// <typeparam name="TDeclaringType">Type declaring property.</typeparam>
            /// <typeparam name="TBaseType">Will be DictionaryBased at run time.</typeparam>
            /// <typeparam name="TConstructedType">Constructed new type.</typeparam>
            /// <param name="property">Name of the property.</param>
            /// <returns>Delegate implementing custom logic.</returns>
            public override Action<TBaseType, TResult> ImplementSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                var propertyName = property.Name;
                //lambdas are easy to debug and implement.
                return (pthis, value) =>
                {
                    pthis._data[propertyName] = (object)value;
                };
            }

            /// <summary>
            /// Static class holding implementation's constructed types.
            /// </summary>
            /// <typeparam name="TInterface">Implemented interface type</typeparam>
            public static class Of<TInterface> where TInterface : class
            {
                /// <summary>
                /// Holding constructed proxy type.
                /// </summary>
                private static readonly Type Type = PropertyProxy.ConstructType<DictionaryBased, Implementation>(typeof(TInterface));

                /// <summary>
                /// Constructor calling delegate's wrapper.
                /// </summary>
                /// <typeparam name="TDelegate">Any delegate type. 
                /// Its return type must be assignable from Type. 
                /// Constructor with corresponding set of parameters must be present.
                /// </typeparam>
                public static class Create<TDelegate>
                {
                    /// <summary>
                    /// Holding delegate, that can be used for creating an instance of proxy implentation.
                    /// </summary>
                    public static readonly TDelegate New = Constructor.Compile<TDelegate>(Type);
                }
            }
        }
    }
}